package com.ruoyi.accounting.service.impl;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.ruoyi.accounting.domain.AccAccount;
import com.ruoyi.accounting.domain.AccAccountingRecord;
import com.ruoyi.accounting.domain.AccClassification;
import com.ruoyi.accounting.domain.dto.AccAccountingRecordImport;
import com.ruoyi.accounting.enums.AccountingRecordTypeEnum;
import com.ruoyi.accounting.enums.ClassificationTypeEnum;
import com.ruoyi.accounting.factory.AccountRecordStrategyFactory;
import com.ruoyi.accounting.mapper.AccAccountingRecordMapper;
import com.ruoyi.accounting.service.AccAccountService;
import com.ruoyi.accounting.service.AccAccountingRecordService;
import com.ruoyi.accounting.service.AccClassificationService;
import com.ruoyi.accounting.service.AccountingRecordStrategy;
import com.ruoyi.common.core.enums.DelFlag;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.security.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 流水Service业务层处理
 *
 * @author ruoyi
 * @date 2025-07-13
 */
@Service
@Slf4j
public class AccAccountingRecordServiceImpl implements AccAccountingRecordService {
    @Autowired
    private AccAccountingRecordMapper accAccountingRecordMapper;

    @Autowired
    private AccClassificationService accClassificationService;

    @Autowired
    private AccountRecordStrategyFactory strategyFactory;

    @Autowired
    AccAccountService accAccountService;

    /**
     * 查询流水
     *
     * @param id 流水主键
     * @return 流水
     */
    @Override
    public AccAccountingRecord selectAccAccountingRecordById(Long id) {
        return accAccountingRecordMapper.selectAccAccountingRecordById(id);
    }

    /**
     * 查询流水列表
     *
     * @param accAccountingRecord 流水
     * @return 流水
     */
    @Override
    public List<AccAccountingRecord> selectAccAccountingRecordList(AccAccountingRecord accAccountingRecord) {
        return accAccountingRecordMapper.selectAccAccountingRecordList(accAccountingRecord);
    }

    /**
     * 新增流水
     *
     * @param accAccountingRecord 流水
     * @return 结果
     */
    @Override
    public int insertAccAccountingRecord(AccAccountingRecord accAccountingRecord) {
        // 检查分类映射关系
        checkTypeMapping(accAccountingRecord);
        //获取策略
        AccountingRecordStrategy strategy = strategyFactory.getStrategy(accAccountingRecord.getRecordType());
        //验证
        strategy.validate(accAccountingRecord);
        // 处理
        strategy.process(accAccountingRecord);
        //初始化基础信息
        accAccountingRecord.setCreateTime(DateUtils.getNowDate());
        accAccountingRecord.setCreateBy(SecurityUtils.getUsername());
        accAccountingRecord.setDelFlag(DelFlag.normal.getCode());
        return accAccountingRecordMapper.insertAccAccountingRecord(accAccountingRecord);
    }


    /**
     * 修改流水
     *
     * @param accAccountingRecord 流水
     * @return 结果
     */
    @Override
    public int updateAccAccountingRecord(AccAccountingRecord accAccountingRecord) {
        accAccountingRecord.setUpdateTime(DateUtils.getNowDate());
        return accAccountingRecordMapper.updateAccAccountingRecord(accAccountingRecord);
    }

    /**
     * 批量删除流水
     *
     * @param ids 需要删除的流水主键
     * @return 结果
     */
    @Override
    public int deleteAccAccountingRecordByIds(Long[] ids) {
        return accAccountingRecordMapper.deleteAccAccountingRecordByIds(ids);
    }

    /**
     * 删除流水信息
     *
     * @param id 流水主键
     * @return 结果
     */
    @Override
    public int deleteAccAccountingRecordById(Long id) {
        return accAccountingRecordMapper.deleteAccAccountingRecordById(id);
    }

    @Override
    public int importFromExcel(MultipartFile file, Long bookId) {
        // 设置导入参数
        ImportParams params = new ImportParams();
        params.setTitleRows(0); // 标题行数
        params.setHeadRows(1);  // 表头行数
        List<AccAccountingRecordImport> importData;
        try (InputStream is = file.getInputStream()) {
            // 使用EasyPoi工具类直接解析Excel
            importData = ExcelImportUtil.importExcel(is, AccAccountingRecordImport.class, params);
        } catch (Exception e) {
            throw new ServiceException("Excel导入失败: " + e.getMessage());
        }

        //导入前的校验

        List<String> types = importData.stream().map(AccAccountingRecordImport::getType).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        if (types.size() > 1) {
            throw new ServiceException("Excel导入失败: 交易类型不一致");
        }

        AccountingRecordTypeEnum accountingRecordTypeEnum = AccountingRecordTypeEnum.getAccountingRecordTypeEnum(types.get(0));
        if (accountingRecordTypeEnum==null) {
            throw new ServiceException("Excel导入失败: 未成功获取交易类型");
        }

        //使用交易类型获取策略
        AccountingRecordStrategy strategy = strategyFactory.getStrategy(accountingRecordTypeEnum.getCode());
        List<AccAccountingRecord> insertRecord = strategy.importFromExcel(importData, bookId);
        if (CollectionUtils.isEmpty(insertRecord)) {
            throw new ServiceException("Excel导入失败: 解析内容失败");
        }

        return accAccountingRecordMapper.batchInsert(insertRecord);
    }

    private void importDataCheck(List<AccAccountingRecordImport> importData, Long bookId) {


        //交易类型
        List<String> AccountingRecordTypeNames = AccountingRecordTypeEnum.getNames();
        //分类 带层级
        List<AccClassification> classificationList = accClassificationService.selectLevelClassificationList(bookId);
        //map<父级名称，子级名称集合>
        Map<String, List<String>> parentToSubNames = classificationList.stream()
                .filter(Objects::nonNull) // 避免空父级
                .collect(Collectors.toMap(
                        AccClassification::getClassificationName,
                        parent -> Optional.ofNullable(parent.getSubClassifications()).orElse(Collections.emptyList())
                                .stream()
                                .map(AccClassification::getClassificationName)
                                .filter(Objects::nonNull)
                                .collect(Collectors.toList())
                ));
        //账户
        AccAccount accAccount = new AccAccount();
        accAccount.setAccountBookId(bookId);
        accAccount.setDelFlag(DelFlag.normal.getCode());
        List<AccAccount> accAccounts = accAccountService.selectAccAccountList(accAccount);
        Map<String, Long> accountMap = accAccounts.stream().collect(Collectors.toMap(AccAccount::getAccountName, AccAccount::getId));
        for (AccAccountingRecordImport record : importData) {
            if (!AccountingRecordTypeNames.contains(record.getType())) {
                throw new ServiceException("交易类型错误");
            }

            if (parentToSubNames.containsKey(record.getCategory())) {
                if (!parentToSubNames.get(record.getCategory()).contains(record.getSubCategory())) {
                    throw new ServiceException("子分类错误");
                }
            } else {
                throw new ServiceException("分类错误");
            }

            if (!accountMap.containsKey(record.getAccount())) {
                throw new ServiceException("支出账户错误");
            }
        }

    }

    /**
     * 校验流水类型和分类的映射关系
     *
     * @param accAccountingRecord
     */
    private void checkTypeMapping(AccAccountingRecord accAccountingRecord) {
        Integer recordType = accAccountingRecord.getRecordType();
        if (Objects.equals(AccountingRecordTypeEnum.INCOME.getCode(), recordType) || Objects.equals(AccountingRecordTypeEnum.EXPENDITURE.getCode(), recordType)) {
            AccClassification accClassification = accClassificationService.selectAccClassificationById(accAccountingRecord.getClassificationId());
            ClassificationTypeEnum classificationTypeEnum = AccountingRecordTypeEnum.getClassificationTypeEnum(recordType);
            if (!Objects.equals(classificationTypeEnum.getCode(), accClassification.getClassificationType())) {
                throw new ServiceException("流水类型和分类类型不匹配");
            }
        }


    }
}
